/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package preka.prefuse.visual;

import java.util.ArrayList;
import java.util.Iterator;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.visual.EdgeItem;
import prefuse.visual.VisualItem;
import preka.IVC;
import weka.clusterers.InstancePair;

/**
 *
 * @author vhbarros
 */
public class VisualInstances {

    private ArrayList m_visualInstances;


    public VisualInstances() {

    }

    public VisualInstances(Visualization vis, String group, int numClasses) {
        createVisualInstances(vis, group);
        //setInitialGroupCenters(vis, group, numClasses);
    }

    public void createVisualInstances(Visualization vis, String group) {

        if (m_visualInstances == null)
            m_visualInstances = new ArrayList();
        else
            m_visualInstances.clear();

        VisualItem vit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            vins = new VisualInstance(vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE),
                                      vit.getX(),
                                      vit.getY(),
                                      false,
                                      vit.getString(IVC.CLASS_ATTRIBUTE));
            if (!m_visualInstances.contains(vins))
                m_visualInstances.add(vins);
        }
    }


    public void setInitialGroupCenters(Visualization vis, String group, int numClasses) {

        Display d = vis.getDisplay(0);
        double xd = d.getDisplayX();
        double yd = d.getDisplayY();

        System.out.println("setInitialGroupCenters xd: "+xd+" yd:"+yd);
        double x=0, y=0;

        VisualInstance vinst;
        VisualItem vit;
        // TODO Melhoria: Está hardcoded e só dá até 4 grupos! Incluir parâmetro de distância de ML
        for (int i=1; i <= numClasses; i++) {

            // 1º Quadrante
            /*if(i == 1)
            {
                x = -1*(xd-xd/3);
                y = -1*(yd-yd/3);
            }

            // 2º Quadrante
            if(i == 2)
            {
                x = (xd-xd/3);
                y = -1*(yd-yd/3);
            }

            // 3º Quadrante
            if(i == 3)
            {
                x = (xd-xd/3);
                y = (yd-yd/3);
            }

            // 4º Quadrante
            if(i == 4)
            {
                x = -1*(xd-xd/3);
                y = (yd-yd/3);
            }*/

            vinst = getVisualInstance((i*-1)); // TODO Retirar -1 quando tirar o nó central --ok!
            vit = getVisualItem(vis, group, (i*-1)); // TODO Retirar -1 quando tirar o nó central --ok!

            // Actualização da instância
            /*vinst.setX(x);
            vinst.setY(y);

            // Actualização do item visual
            vit.setX(x);
            vit.setY(y);
            vit.setFixed(true);*/
            vit.setInteractive(false);

        }
    }


    public VisualInstance getVisualInstance(int numInstance) {

        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            if (vinst.getNumInstance() == numInstance)
                return vinst;
        }

        vinst = null;
        return vinst;
    }

    public VisualItem getVisualItem(Visualization vis, String group, int numInstance) {

        VisualItem vit = null;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            if (vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE) == numInstance)
                return vit;
        }

        return vit;
    }

    public int getNumMovedInstances() {

        int numMovedInstances = 0;
        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            if (vinst.hasMoved())
                numMovedInstances++;
        }

        return numMovedInstances;
    }

    public int[] getMovedInstances() {

        int numMovedInstances = getNumMovedInstances();

        if (numMovedInstances == 0)
            return null;

        int[] movedInstances = new int[numMovedInstances];
        int i = 0;
        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            if (vinst.hasMoved()) {
                movedInstances[i] = vinst.getNumInstance();
                i++;
            }

        }

        return movedInstances;
    }

    public int[] getClusterMovedInstances(Visualization vis, String group, int numClusterInstance) {

        ArrayList mi = new ArrayList();
        int i = 0, target;
        VisualItem vit;
        EdgeItem eit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            eit = (EdgeItem) vit;

            if (vit.getSourceTuple().getString(IVC.EDGE_TYPE_ATTRIBUTE).equals(IVC.EDGE_CLUSTERING) &&
                eit.getSourceItem().getInt(IVC.INSTANCE_ATTRIBUTE) == numClusterInstance) {

                target = eit.getTargetItem().getInt(IVC.INSTANCE_ATTRIBUTE);
                vins = getVisualInstance(target);

                if (vins.hasMoved())
                    mi.add(target);
            }
        }

        int[] movedInstances = new int[mi.size()];
        items = mi.iterator();
        while (items.hasNext()) {
            movedInstances[i] = (Integer) items.next();
            i++;
        }

        return movedInstances;
    }

    public int getClusterInstance(Visualization vis, String group, int numInstance) {

        VisualItem vit;
        EdgeItem eit;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            eit = (EdgeItem) vit;

            if (vit.getSourceTuple().getString(IVC.EDGE_TYPE_ATTRIBUTE).equals(IVC.EDGE_CLUSTERING) &&
                eit.getTargetItem().getInt(IVC.INSTANCE_ATTRIBUTE) == numInstance)
                return eit.getSourceItem().getInt(IVC.INSTANCE_ATTRIBUTE);
        }

        return -9999;
    }

    public ArrayList getMovedVisualInstances() {

        ArrayList movedVisualInstances = new ArrayList();

        VisualInstance vinst;
        Iterator items = m_visualInstances.iterator();
        while (items.hasNext()) {
            vinst = (VisualInstance) items.next();

            if (vinst.hasMoved()) {
                if (!movedVisualInstances.contains(vinst))
                    movedVisualInstances.add(vinst);
            }
        }

        return movedVisualInstances;
    }


    public int getNumCenterVisualInstances() {

        int numCenterInstances = 0;
        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            if (vinst.getNumInstance() < 0)
                numCenterInstances++;
        }

        return numCenterInstances;
    }


    public ArrayList getCenterVisualInstances() {

        ArrayList centerVisualInstances = new ArrayList();

        VisualInstance vinst;
        Iterator items = m_visualInstances.iterator();
        while (items.hasNext()) {
            vinst = (VisualInstance) items.next();

            if (vinst.getNumInstance() < 0) {
                if (!centerVisualInstances.contains(vinst))
                    centerVisualInstances.add(vinst);
            }
        }

        return centerVisualInstances;
    }

    public ArrayList getVisualInstances() {

        ArrayList visualInstances = new ArrayList();

        VisualInstance vinst;
        Iterator items = m_visualInstances.iterator();
        while (items.hasNext()) {
            vinst = (VisualInstance) items.next();
            visualInstances.add(vinst);
        }

        return visualInstances;
    }

    public ArrayList getMovedVisualItems(Visualization vis, String group) {

        ArrayList movedVisualItems = new ArrayList();

        VisualItem vit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            int instance = vit.getInt(IVC.INSTANCE_ATTRIBUTE);
            vins = getVisualInstance(instance);

            //System.out.println(" -- instância: "+vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE));

            if (instance > -1) // os centros não devem ser considerados
                if (vins.hasMoved()) {
                    if (!movedVisualItems.contains(vit))
                        movedVisualItems.add(vit);
                }
        }

        return movedVisualItems;
    }

    public ArrayList getNotMovedVisualInstances(Visualization vis, String group) {
        return getNotMovedVisualInstances(vis, group, null);
    }

    public ArrayList getNotMovedVisualInstances(Visualization vis, String group, String className) {

        ArrayList notMovedVisualInstances = new ArrayList();

        VisualItem vit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            int instance = vit.getInt(IVC.INSTANCE_ATTRIBUTE);
            vins = getVisualInstance(instance);

            //System.out.println(" -- instância: "+vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE));

            if (instance > -1) // os centros não devem ser considerados
                if (!vins.hasMoved())
                    if (className == null || vins.getClassName().equals(className))
                        if (!notMovedVisualInstances.contains(vins))
                            notMovedVisualInstances.add(vins);
        }

        return notMovedVisualInstances;
    }

    public void removeVisualInstance(int numInstance) {

        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            if (vinst.getNumInstance() == numInstance)
                m_visualInstances.remove(vinst);
        }

    }

    public void addVisualInstance(VisualInstance instance) {

        if(!m_visualInstances.contains(instance))
            m_visualInstances.add(instance);

    }


    public void updateClusters(int numInstances, int[] clusterAssignments){

        VisualInstance vinst;
        for (int i = 0; i < numInstances; i++) {
            vinst = getVisualInstance(i);
            vinst.setCluster(clusterAssignments[i]);
            vinst.setVisualCluster((clusterAssignments[i]+1)*-1);
        }

    }

    public void updateVisualClusters(int numInstances, int[] clusterAssignments){

        VisualInstance vinst;
        for (int i = 0; i < numInstances; i++) {
            vinst = getVisualInstance(i);
            vinst.setCluster((clusterAssignments[i]+1)*-1);
            vinst.setVisualCluster(clusterAssignments[i]);
        }

    }


    public void updatePositions(Visualization vis, String group) {

        int instance;
        VisualItem vit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            instance = vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE);
            vins = getVisualInstance(instance);

            // Os centros de grupos não são processados
            if (instance > -1) {
                // Actualiza a posição
                vins.setX(vit.getX());
                vins.setY(vit.getY());
            }
        }

    }

    public void applyPositions(Visualization vis, String group, ArrayList constraints) {

        int instance;
        VisualItem vit;
        VisualInstance vins;
        Iterator items = vis.items(group);
        while (items.hasNext()) {
            vit = (VisualItem) items.next();
            instance = vit.getSourceTuple().getInt(IVC.INSTANCE_ATTRIBUTE);
            vins = getVisualInstance(instance);

           if (instance < 0) // Centros de grupos
                applyCenterPosition(vit, vins, instance);
           else
                applyInstancePosition(vit, vins);

        }

    }

    public void applyInstancePosition(VisualItem vit, VisualInstance vins) {

        // Actualiza a posição
        vit.setX(vins.getX());
        vit.setY(vins.getY());

        // Fixa caso tenha sido movido
        vit.setFixed(vins.hasMoved());

    }

    public void applyCenterPosition(VisualItem vit,
                                    VisualInstance vins,
                                    int instance) {

        // TODO remover debug
        int clusterCenter = (instance+1)*-1;
                System.out.println("Processando o centro: "+instance+" aka:"+clusterCenter);

        ArrayList movedInstances = new ArrayList();
        double numInstancesAffected = 0, x = 0, y = 0;

        // Adiciona as instâncias movidas, para processamento
        movedInstances = getMovedVisualInstances();
        
        VisualInstance vinst;
        Iterator it = movedInstances.iterator();
        while (it.hasNext()) {

            System.out.println("Processando a instância: "+instance);
            vinst = (VisualInstance) it.next();

            if (vinst.getVisualCluster() == instance) {
                System.out.println(" --- eu ("+vinst.getNumInstance()+") sou do cluster "+instance+" aka:"+vinst.getCluster());
                x += vinst.getX();
                y += vinst.getY();
                numInstancesAffected++;
            }

        }

        if (numInstancesAffected != 0) {

            // Calcula o ponto médio entre as instâncias
            x = x/numInstancesAffected;
            y = y/numInstancesAffected;

            // Actualiza a posição do centro
            vins.setX(x);
            vins.setY(y);
            vit.setX(x);
            vit.setY(y);
            vit.setFixed(true);
            vit.setInteractive(false);
        }
        else {
            x = 0;
            y = 0;

            vins.setX(x);
            vins.setY(y);
            vit.setX(x);
            vit.setY(y);
            vit.setFixed(true);
            vit.setInteractive(false);
        }

    }

    public void printVisualInstances() {

        VisualInstance vinst;
        Iterator vinsts = m_visualInstances.iterator();
        while (vinsts.hasNext()) {
            vinst = (VisualInstance) vinsts.next();
            System.out.println("- instância: "+vinst.getNumInstance()+
                               " x:"+vinst.getX()+
                               " y:"+vinst.getY()+
                               " moved:"+vinst.hasMoved()+
                               " cluster:"+vinst.getCluster());
        }
    }

}
